﻿using System;
using System.IO.Compression;
using System.Net;
using System.Web;
using System.Web.Configuration;

namespace CssAndJsCombiner
{
    public class WebContext
    {
        private HttpContext _ctx;

        public bool IsPreview { get; set; }
        public object DocumentID { get; set; }

        public static WebContext Current
        {
            get
            {
                if (HttpContext.Current == null) return null;
                
                WebContext ctx = HttpContext.Current.Items["__ctx__"] as WebContext;
                if (ctx == null)
                {
                    ctx = new WebContext(HttpContext.Current);
                    HttpContext.Current.Items["__ctx__"] = ctx;
                }

                return ctx;
            }
        }

        public static bool IsDebugMode
        {
            get
            {
                SystemWebSectionGroup configGroup = WebConfigurationManager.OpenWebConfiguration("~").GetSectionGroup("system.web") as SystemWebSectionGroup;
                return configGroup.Compilation.Debug;
            }
        }

        public bool IsAuthenticated
        {
            get { return _ctx.User.Identity.IsAuthenticated; }
        }

        public WebContext(HttpContext ctx)
        {
            this._ctx = ctx;
            this.IsPreview = false;
        }

        public static string MapPath(string path)
        {
            string ret = path;

            if (HttpContext.Current != null)
            {
                ret = HttpContext.Current.Server.MapPath(path);
            }
            else
            {
                ret = VirtualPathUtility.ToAppRelative(path, HttpRuntime.AppDomainAppVirtualPath);
            }

            return ret;
        }

        public static string ResolveClientUrlAbsolute(string url)
        {
            string vPath = HttpRuntime.AppDomainAppVirtualPath;
            return url.Replace("~/", vPath.EndsWith("/") ? vPath : vPath + "/");
        }

        /// <summary>
        /// Writes the Last-Modified header and sets the conditional get headers.
        /// </summary>
        /// <param name="lastModified">The date of the last modification.</param>
        public void SetConditionalGetHeaders(DateTime lastModified)
        {
            HttpResponse response = _ctx.Response;
            HttpRequest request = _ctx.Request;

            string incomingDate = request.Headers["If-Modified-Since"];

            response.Cache.SetLastModified(lastModified);

            DateTime testDate = DateTime.MinValue;
            if (DateTime.TryParse(incomingDate, out testDate) && testDate == lastModified)
            {
                response.ClearContent();
                response.StatusCode = (int)HttpStatusCode.NotModified;
                response.End();
            }
        }

        public void SetCache(string content, int days)
        {
            HttpCachePolicy cache = _ctx.Response.Cache;

            cache.SetETag("\"" + content.GetHashCode() + "\"");
            cache.VaryByHeaders["Accept-Encoding"] = true;
            cache.SetExpires(DateTime.Now.ToUniversalTime().AddDays(days));
            cache.SetCacheability(HttpCacheability.Public);
            cache.SetMaxAge(new TimeSpan(days, 0, 0, 0));
            cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
        }

        /// <summary>
        /// Compresses the HttpContext's output stream using either Deflate or GZip.
        /// </summary>
        /// <param name="context">The current HTTP context to compress.</param>
        public void Compress()
        {
            if (IsEncodingAccepted(DecompressionMethods.Deflate))
            {
                _ctx.Response.Filter = new DeflateStream(_ctx.Response.Filter, CompressionMode.Compress);
                SetEncoding(DecompressionMethods.Deflate);
            }
            else if (IsEncodingAccepted(DecompressionMethods.GZip))
            {
                _ctx.Response.Filter = new GZipStream(_ctx.Response.Filter, CompressionMode.Compress);
                SetEncoding(DecompressionMethods.GZip);
            }
        }

        /// <summary>
        /// Checks the request headers to see if the specified
        /// encoding is accepted by the client.
        /// </summary>
        /// <param name="encoding">The name of the encoding to check for.</param>
        /// <returns>True if the client supports the specified encoding; otherwise false.</returns>
        public bool IsEncodingAccepted(DecompressionMethods compression)
        {
            string encoding = compression.ToString().ToLower();

            return _ctx.Request.Headers["Accept-encoding"] != null && _ctx.Request.Headers["Accept-encoding"].Contains(encoding);
        }

        /// <summary>
        /// Adds the specified encoding to the response headers.
        /// </summary>
        /// <param name="encoding">The encoding to sent to the Accept-encoding HTTP header of the response.</param>
        public void SetEncoding(DecompressionMethods compression)
        {
            string encoding = compression.ToString().ToLower();

            _ctx.Response.AppendHeader("Content-encoding", encoding);
            _ctx.Response.Cache.VaryByHeaders["Accept-encoding"] = true;
        }
    }
}
